package net.j_davis.dice;

import java.util.Calendar;
import java.util.Random;
import java.util.Scanner;

/**
 * Provides methods related to d20 dice rolls.
 */
public abstract class Dice {
   private static Random rand = new Random(Calendar.getInstance().getTimeInMillis());
   
   private final static int NOT_PROCESSED = -1;
   
   /**
    * Returns a random roll specified by the rollString.
    * 
    * @param rollString Roll string in the format: ndx(+/-)y,
    *        where n = number of dice to roll,
    *        where x = number of sides, and
    *        where (+/)y = optional roll modifier.
    * @return Result of the roll.
    * @throws InvalidRollStringException Thrown when the rollString is invalid.
    */
   public static int d(String rollString) throws InvalidRollStringException {
      Scanner scn = new Scanner(rollString).useDelimiter("[d+-]");
      
      int numDice = NOT_PROCESSED;
      int numSides = NOT_PROCESSED;
      int modifier = NOT_PROCESSED;
      
      if (scn.hasNextInt()) {
         numDice = scn.nextInt();
      }
      
      if (scn.hasNextInt()) {
         numSides = scn.nextInt();
      }
      
      if (scn.hasNextInt()) {
         modifier = scn.nextInt();
      }
      else {
         modifier = 0;
      }
      
      if (numDice == NOT_PROCESSED ||
            numSides == NOT_PROCESSED ||
            modifier == NOT_PROCESSED) {
         throw new InvalidRollStringException(rollString);
      }
      
      return d(numDice, numSides) + modifier;
   }
   
   
   /**
    * Returns a random roll specified by the rollString.
    * 
    * @param rollString Roll string in the format: ndx(+/-)y,
    *        where n = number of dice to roll,
    *        where x = number of sides, and
    *        where (+/)y = optional roll modifier.
    * @return Result of the roll.
    * @throws InvalidRollStringException Thrown when the rollString is invalid.
    */
   /*public static int d(String rollString) throws InvalidRollStringException {
      // Remove all whitespace from string and prepare for processing
      rollString = rollString.replaceAll("\\s", "");
      
      // Load a queue with the rollString.
      LinkedList<Character> procQueue = new LinkedList<Character>();
      for (int i = 0; i < rollString.length(); i++) {
         procQueue.add(rollString.charAt(i));
      }
      
      // Construct roll components
      int countOfDie = NOT_PROCESSED;
      int dieSides = NOT_PROCESSED;
      int modifier = NOT_PROCESSED;
      
      String workingStr = "";
      
      // Get countOfDie
      if (!procQueue.isEmpty() && !Character.isDigit(procQueue.element())) {
         throw new InvalidRollStringException(rollString);
      }
      else {
         workingStr += procQueue.remove();
      }
      
      while (!procQueue.isEmpty() && Character.isDigit(procQueue.element())) {
         workingStr += procQueue.remove();
      }
      
      countOfDie = Integer.parseInt(workingStr);
      workingStr = "";
      
      // Next char should be 'd'
      if (!procQueue.isEmpty() && procQueue.element() != 'd') {
         throw new InvalidRollStringException(rollString);
      }
      else {
         procQueue.remove();
      }
      
      // Get dieSides
      while (!procQueue.isEmpty() && Character.isDigit(procQueue.element())) {
         workingStr += procQueue.remove();
      }
      
      dieSides = Integer.parseInt(workingStr);
      workingStr = "";
      
      // Get roll modifier
      if (!procQueue.isEmpty() && (procQueue.element() == '+' || procQueue.element() == '-')) {
         // Parse the '-' sign if present, otherwise discard modifier
         // character.
         if (procQueue.element() == '-') {
            workingStr += procQueue.remove();
         }
         else {
            procQueue.remove();
         }
         
         while (!procQueue.isEmpty() && Character.isDigit(procQueue.element())) {
            workingStr += procQueue.remove();
         }
         modifier = Integer.parseInt(workingStr);
      }
      else {
         modifier = 0;
      }
      
      if (countOfDie == NOT_PROCESSED ||
            dieSides == NOT_PROCESSED ||
            modifier == NOT_PROCESSED) {
         throw new InvalidRollStringException(rollString);
      }
      
      return rand.nextInt((countOfDie * dieSides) - countOfDie + 1) + countOfDie + modifier;
   } */
   
   /**
    * Returns a random roll, multiple dice.
    *
    * @param sides Number of die sides.
    * @param diceCount Number of dice to roll.
    **/
   public static int d(int diceCount, int sides)
   {
      return rand.nextInt((diceCount * sides) - diceCount + 1) + diceCount;
   }
   
   /**
    * Returns a random roll, a single die with the specified sides.
    *
    * @param sides Number of die sides.
    **/
   public static int d(int sides)
   {
      return rand.nextInt(sides) + 1;
   }
   
   /**
    * Returns a random roll, a single d2 die.
    **/
   public static int d2()
   {
      return rand.nextInt(2) + 1;
   }
   /**
    * Returns a random roll, a single d3 die.
    **/
   public static int d3()
   {
      return rand.nextInt(3) + 1;
   }
   
   /**
    * Returns a random roll, a single d4 die.
    **/
   public static int d4()
   {
      return rand.nextInt(4) + 1;
   }
   
   /**
    * Returns a random roll, a single d6 die.
    **/
   public static int d6()
   {
      return rand.nextInt(6) + 1;
   }
   
   /**
    * Returns a random roll, a single d8 die.
    **/
   public static int d8()
   {
      return rand.nextInt(8) + 1;
   }
   
   /**
    * Returns a random roll, a single d10 die.
    **/
   public static int d10()
   {
      return rand.nextInt(10) + 1;
   }
   
   /**
    * Returns a random roll, a single d12 die.
    **/
   public static int d12()
   {
      return rand.nextInt(12) + 1;
   }
   
   /**
    * Returns a random roll, a single d20 die.
    **/
   public static int d20()
   {
      return rand.nextInt(20) + 1;
   }
   
   /**
    * Returns a random roll, a single d100 die.
    **/
   public static int d100()
   {
      return rand.nextInt(100) + 1;
   }
}